/**
 * Created by spatra on 14-12-2.
 */

define(['angular', 'commonJS/module'], function(angular, commonModule){

  commonModule.factory('MainTopNavInitService', function(){
    return {
      getFunctionNavItems: function(){
        return [
          {name: 'project', uiSerf: 'project.list', label: '我的项目'},
          {name: 'personal', uiSerf: 'personal.information.setting', label: '个人主页'}
        ];
      }
    };
  });

  /**
   * 资源访问服务, 使用范例:
   *
   * var accessor = ResourceService.getAccessor({
   *  'resourceName': 'student',
   *  'apiPrefix': 'api_prefix'
   *  });
   *
   *  var list;
   *  accessor.get().success(data){
   *   list = data;
   *  });
   *
   * 则会向地址: /api_prefix/student的发送REST风格的交互请求, 通过方法 getBaseUrl 可以获得此地址
   * 方法包含: get/show/update/destroy(这些方法都返回 HTTP Promise对象)
   *
   * 注意: apiPrefix为非必填项, 默认为`api`
   *
   * 可以通过方法: getFactoryFunction 得到工厂构造函数
   */
  commonModule.factory('ResourceService',['$http', function($http){

    function resourceFactory(options){
      this.resourceName = options['resourceName'];
      this.apiPrefix = options['apiPrefix'] || 'api';
    }

    resourceFactory.prototype = {
      constructor: resourceFactory,
      get: function(getOps){
        return $http.get( this.getBaseUrl(), getOps );
      },
      store: function(resourceData){
        return $http.post( this.getBaseUrl(), resourceData);
      },
      show: function(resourceId, getOps){
        return $http.get( this.getBaseUrl() + '/' + resourceId, getOps );
      },
      update: function(resourceId, resourceData){
        return $http.put( this.getBaseUrl() + '/' + resourceId, resourceData);
      },
      destroy: function(resourceId){
        return $http.delete( this.getBaseUrl() + '/' + resourceId );
      },
      getBaseUrl: function(){
        if( ! this.baseUrl ){
          this.baseUrl = [
            this.apiPrefix, this.resourceName
          ].join('/');
        }

        return this.baseUrl;
      }
    };

    return {
      getResourceAccessor: function(options){
        return new resourceFactory(options);
      },
      getFactoryFunction: function(){
        return resourceFactory;
      }
    }

  }]);

  /**
   * 嵌套资源访问服务, 在CURD的使用上与ResourceService基本一致.
   *
   * 所谓的嵌套资源, 例如具体的项目会包含一定的任务,任务必须属于某一项目下,则任务是项目的嵌套资源.
   *
   * 使用范例:
   *
   * var accessor = NestedResourceService.getAccessor({
   *  'parentResourceName': 'project',
   *  'nestedResourceName': 'task',
   *  apiPrefix': 'api' //api为默认值, 可不填写
   * });
   *
   * accessor.setParentResourceId({
   *  $stateParams, // $stateParams也可以不必是ng自带服务,但需要是具有`projectId`属性的对象
   *  'projectId' // 此处可不填写,默认由 `parentResourceName` 加上 `Id`组合而成,用于访问$stateParams中的url参数
   * });
   *
   * 注意，如果不显式设定父级资源，则会尝试自动通过$stateParams来自动获取，其所访问的命名形式为：  `parentResourceName` 加上 `Id` 组合而成
   *
   * 如果出现了三层以上的多级嵌套，则可写多个父级资源。例如，父级资源(使用"|"来分隔)： "project|discussion", 嵌套资源："comments",
   * setParentResourceId处也采用同样的命名方式.
   *
   * 则会向地址: /api/project/1/task 的发送REST风格的交互请求(此处假定`projectId`的值为:1), 通过方法 getBaseUrl 可以获得此地址
   * 方法包含: get/show/update/destroy. get/show/update/destroy(这些方法都返回 HTTP Promise对象)
   *
   *  var list;
   *  accessor.get().success(data){
   *   list = data;
   *  });
   *
   *  备注: NestResourceService实际上是基于对ResourceService中 getBaseUrl 方法的扩展.
   */
  commonModule.factory('NestedResourceService', ['$http', '$stateParams', 'ResourceService', 'ClassHelperService',
    function($http, $stateParams, ResourceService, ClassHelperService){

      function nestResourceFactory(options){
        this.parentResourceNames = options['parentResourceName'].split('|');
        this.nestedResourceName = options['nestedResourceName'];
        this.apiPrefix = options['apiPrefix'] || 'api';
      }

      ClassHelperService.extend(nestResourceFactory, ResourceService.getFactoryFunction());

      ClassHelperService.extendOrOverloadMethod(nestResourceFactory, 'setParentResourceId', function(stateParams, parentResourceIdDesces){
        var self = this;
        var requestUrlArray = [self.apiPrefix];

        if( parentResourceIdDesces ){
          //如果用户设定了特殊的描述则执行此处

          if( parentResourceIdDesces.indexOf('*') !== -1 &&
            parentResourceIdDesces.length == self.parentResourceNames.length ){

            parentResourceIdDesces = parentResourceIdDesces.split('|');
            for(var i = 0, length = parentResourceIdDesces.length; i < length; ++i ){
              if( parentResourceIdDesces[i] === '*' ){
                parentResourceIdDesces[i] = self.parentResourceNames[i] + 'Id';
              }

              requestUrlArray.push(self.parentResourceNames[i]);
              requestUrlArray.push(stateParams[ parentResourceIdDesces[i] ]);
            }
          }

        }
        else{

          self.parentResourceNames.forEach(function(item){
            requestUrlArray.push(item);
            requestUrlArray.push(stateParams[ item + 'Id' ]);
          });
        }

        requestUrlArray.push(self.nestedResourceName);

        self.parentResourceId = 'Init';
        self.requestUrl = requestUrlArray.join('/');
      });

      ClassHelperService.extendOrOverloadMethod(nestResourceFactory, 'getBaseUrl', function(){
        var self = this;

        if( ! self.parentResourceId ){
          self['setParentResourceId']($stateParams);
        }

        return self.requestUrl;
      });

      return {
        getResourceAccessor: function(options){
          return new nestResourceFactory(options);
        },
        getFactoryFunction: function(){
          return nestResourceFactory;
        }
      };
  }]);

  /**
   * 模拟典型的类继承(C++/Java风格)的辅助服务.
   *
   * 方法: extend , 用于模拟继承机制, 范例:
   *  function Parent(){}
   *  function Child(){}
   *  ClassHelperService.extend(Child, Parent);
   *
   * 方法: extendOrOverloadMethod, 用于重写类方法:
   *
   *  function Parent(){}
   *  Parent.prototype.parentMethod = function(){
   *   return 'Parent';
   *  }
   *  function Child(){}
   *
   *  ClassHelperService.extend(Child, Parent);
   *  ClassHelperService.extendOrOverloadMethod(Child, 'parentMethod', function(){
   *    return 'Child';
   *  });
   *
   *  方法： objectEquals, 用于比较两个类对象是否相等, 如果相等则返回true，否则返回false
   *
   *  方法： update，将(from)对象上的内容，添加或覆盖到(to)对象上.
   *
   *  方法： isEmpty, 判断对象是否为空（如果对象没有任何属性，又或者对象的属性逻辑全都是逻辑为真，则结果为true)
   *
   *  方法： objListToSet， 将对象数组转换成 key-value 的集合，第一个参数为列表（或类数组对象）， 第二个参数为作为key的字段.
   *     var objList = [{"name": "Jack", "age": 30}, {"name": "Mike", "age": 22}];
   *     var set = objListToSet(objList, 'name');
   *     //set 的形式为： {"Jack": {"name": "Jack", "age": 30}, "Mike": {"name": "Mike", "age": 22} }
   *
   * 方法： setToList， 将key-value的集合转换成列表形式，是 objListToSet 的相反操作.
   */
  commonModule.factory('ClassHelperService', function(){
    var explodes = {};

    explodes['extend'] = function(child, parent){
      var agent = function(){};

      agent.prototype = parent.prototype;
      child.prototype = new agent();
      child.prototype.constructor = child;
      child.uber = parent.prototype;
    };

    explodes['extendOrOverloadMethod'] = function(constructor, methodName, methodFun){
      constructor.prototype[ methodName ] = methodFun;
    };

    explodes['objectEquals'] = function objectEquals(lhs, rhs){
      if( ! angular.isObject(lhs) || ! angular.isObject(rhs) ){
        return lhs === rhs;
      }

      for(var prop in lhs){
        if( ! (prop in rhs) ){
          return false;
        }

        if( typeof lhs[prop] === 'object' && ! objectEquals(lhs[prop], rhs[prop]) ){
          return false;
        }
        else if(lhs[prop] !== rhs[prop] ){
          return false;
        }
      }

      return true;
    };

    explodes['clone'] = function clone(obj){
      if( typeof obj !== 'object' || obj === null ){
        return obj;
      }

      var newObj = new obj.constructor;
      for( var item in obj ){
        if( typeof obj[ item ] === 'object' ){
          newObj[ item ] = clone( obj[ item ]);
        }
        else{
          newObj[ item ] = obj[ item ];
        }
      }

      return newObj;
    };

    explodes['update'] = function update(from, to){
      for(var prop in from ){
        if( ! explodes['objectEquals']( from[prop], to[prop] ) ){
          to[prop] = explodes['clone']( from[prop] );
        }
      }
    };

    explodes['isEmpty'] = function(obj){
      for(var prop in obj ){
        if( ! obj[prop] ){
          return false;
        }
      }

      return true;
    };

    explodes['setToList'] = function(setObj){
      var rtn = [];

      for(var prop in setObj ){
          rtn.push( setObj[prop] );
      }

      return rtn;
    };

    explodes['objListToSet'] = function(list, prop){
      var rtn = {};

      list.forEach(function(item){
        rtn[ item[prop] ] = item;
      });

      return rtn;
    };

    return explodes;
  });

  /**
   * 此服务用于配合angular-ui-bootstrap-datePicker使用，提供一些默认的设置
   */
  commonModule.factory('NgUIDatePickerService', function(){

    /**
     * 构造函数，生成用于设定的对象
     *
     * @param settingObj
     * @constructor
     */
    function DatePickerSetting(settingObj){
      settingObj = settingObj || {};

      var currentDate = new Date;

      this.date = currentDate.toISOString().split("T")[0];
      this.minDate =currentDate;

      this.dateFormat = settingObj.dateFormat || 'dd-MMMM-yyyy';
      this.dateOptions = settingObj.dateOptions || {formatYear: 'yy', startingDay: 6};
    }

    DatePickerSetting.prototype = {
      open: function($event){
        $event.preventDefault();
        $event.stopPropagation();

        this.opened = true;
      },
      disabled: function(date, mode){
        return ( mode === 'day' && ( date.getDay() === 0 || date.getDay() === 6 ) );
      },
      getDate: function(){
        var rtn = this.date.toISOString ? this.date.toISOString().split("T")[0] : this.date;
        return rtn;
      }
    };

    return {
      getDefault: function(){
        return new DatePickerSetting();
      },
      get: function(settingObj){
        return new DatePickerSetting(settingObj);
      }
    };
  });

  /**
   * 用户管理用户状态的服务，可以用于获取用户信息.
   *
   * 使用示范，获取用户的电子邮件地址地址：
   * var email LoginStatusService.get('userInfo.email');
   *
   * 所对应的JSON为： {userInfo: {email: 'xx@qq.com'}}
   *
   * 具体的数据后台通过cookie设定，前端获取后删除该cookie
   */
  commonModule.factory('LoginStatusService', ['$rootScope', '$cookieStore', '$http', '$interval', 'ClassHelperService',
    function($rootScope, $cookieStore, $http, $interval, ClassHelperService){
      var loginData = null;

      return {
        _registerOnRootScope: function(){
          var self = this;

          $rootScope.$on('personalInfo:update', self.updatePersonalInfo);
          $rootScope.$on('unread:update', self.updateUnreadStatistics);
          $rootScope.$on('task:reload', self.updateUnreadStatistics);
        },
        init: function(key){
          key = key || 'loginData';
          loginData = $cookieStore.get(key);
          $cookieStore.remove(key);

          if( ! loginData ){
            throw '无法获取用户信息';
          }

          var self = this;

          //当出现个人信息更改的操作时，重新获取新的个人信息
          $rootScope.$on('personalInfo:update', function(){

          });

          //注册事件
          self._registerOnRootScope();

          //按每2分钟一次,检查是否有新的私信或通知
          $interval(function(){
            self.updateUnreadStatistics();
          }, 1000 * 60 * 2);
        },
        get: function(item, separator){
          separator = separator || '.';

          var arr = item.split(separator), rtn = loginData;
          for(var i = 0, length = arr.length; i < length; ++i ){
            rtn = rtn[ arr[i] ];
          }

          return rtn;
        },
        logout: function(){
          window.location = this.get('uri.logout');
        },
        //更新未读私信或通知
        updateUnreadStatistics: function(){
          $http.get('ng/unread')
            .success(function(data){
              var unread = loginData['unread'];

              if( ! ClassHelperService.objectEquals(unread, data) ){
                unread.notification = data.notification;
                unread.message = data.message;

                if( unread.notification > data.notification || unread.message > data.message ){
                  $rootScope.$emit('message:info', {
                    msg: '有新的通知或私信，请查阅'
                  });
                }

              }

            })
            .error(function(data){
              $rootScope.$emit('message:error', {
                title: '请检查网络',
                msg: '更新未读私信或通知失败'
              });
              console.error(data);
            });
        },
        //更新个人信息
        updatePersonalInfo: function(){
          $http.get('api/personal/info')
            .success(function(data){ ClassHelperService.update(data, loginData['personalInfo']) })
            .error(function(data){ console.error(data); });
        }
      };
  }]);

  /**
   * 用户相关服务，包含下列方法：
   *
   * exist: 传入用户的用户名或电子邮件地址，即可查询该用户是否存在, 取得用户的基本信息
   */
  commonModule.factory('UserService', ['$http', function($http){
    var baseUrl = 'api/user/';

    return {
      exist: function(queryKey){
        return $http.get(baseUrl + 'exist/' + queryKey);
      },
      info: function(userId){
        return $http.get(baseUrl + 'info/' + userId);
      }
    };

  //End of --> UserService
  }]);

  /**
   * 日期与时间相关的辅助函数
   */
  commonModule.factory('DatetimeHelperService', function(){

    //Unix纪元开始的1970-01-01是周四
    var now = null, oneDayMSeconds = 1000 * 60 * 60 *24, unixStartWeekDay = 4;

    /**
     * 返回当前时间，如果没有设定过，则新建，否则返回已经设定的值
     * @returns {*}
     */
    function getNow(){
      if( ! now ){
        return new Date;
      }
      else{
        return now;
      }
    }

    /**
     * 返回JS的Date对象
     *
     * @param source 这个参数用于构造Date对象，如果本身已经是则直接返回
     * @returns {*}
     */
    function getDatetimeObj(source){
      if( source === null || source === undefined ){
        return null;
      }
      else if( ! (source instanceof Date) ){
        return new Date(source);
      }
      else{
        return source;
      }
    }

    /**
     * 检查两个时间是否处于同一个年月.
     *
     * @param lhs
     * @param rhs
     * @returns {boolean}
     */
    function checkMonthAndYear(lhs, rhs){
      return lhs.getMonth() === rhs.getMonth() && lhs.getYear() === rhs.getYear();
    }

    /**
     * 检查两个时间是否处于同一个星期
     *
     * @param lhs
     * @param rhs
     * @returns {boolean}
     */
    function checkSameWeek(lhs, rhs){
      var lhsCounter = parseInt( lhs.getTime() / oneDayMSeconds),
        rhsCounter = parseInt( rhs.getTime() / oneDayMSeconds );

      return parseInt( (lhsCounter + unixStartWeekDay) / 7 ) === parseInt( (rhsCounter + unixStartWeekDay ) / 7 );
    }

    /**
     * 检查是否处于两天之内.
     *
     * @param checkTime 待检查的时间
     * @param timePoint 开始计算的时间点，一般为当前时间点
     * @returns {boolean}
     */
    function checkTowDays(checkTime, timePoint){
      timePoint = timePoint || getNow();

      var timePointDayMSeconds = timePoint.getTime() -
        ( timePoint.getHours() * 60 * 60 + timePoint.getMinutes() * 60 + timePoint.getSeconds() ) * 1000;

      return checkTime.getTime() >= timePointDayMSeconds &&
          checkTime.getTime() <= timePointDayMSeconds + 2 * oneDayMSeconds;
    }

    return {
      //设置当前的时间点，以此作为比较的基准，如果不存入参数或存入null则以运行时时间为当前时间点
      setNow: function(datetime){
        now = getDatetimeObj(datetime);
      },
      //获得当前时间点
      getNow: getNow,
      /*
        判断当期时间点与指定时间是否处于同一个范围:
         'today': 今天之内,
         'twodays': 两天之内,
         'week': 属于同一个星期,
         'month': 属于同一个年月
       */
      checkRange: function(datetime, range){
        datetime = getDatetimeObj(datetime);

        var result = false, now = getNow();

        switch (range){
          case 'today':
            result = datetime.getDate() === now.getDate() && checkMonthAndYear(datetime, now);
            break;
          case 'twodays':
            result =  checkTowDays(datetime, now);
            break;
          case 'week':
            result = checkSameWeek(datetime, now);
            break;
          case 'month':
            result = checkMonthAndYear(datetime, now);
            break;
          default :
            console.error('invalid time change');
            break;
        }

        return result;
      }
    };
  });

  /**
   * 当列表过滤完全基于后台实现时，此服务用于获取后台提供的过滤条件对象.
   *
   * 对象的格式：
   * {
   *    "过滤条件1":
   *    [
   *      {"cond": "过滤条件1的某个值", "label": "过滤条件1的某个值的文本显示"},
   *      //注意是个数组...
   *    ],
   *    "过滤条件2":
   *    //........
   * }
   */
  commonModule.factory('BackendFilterService', ['$http', function($http){
    var baseUrl = 'api/backend-filter/';

    return {
      getMethods: function(query){
        return $http.get(baseUrl + query);
      }
    };
  }]);//End of --> BackendFilterService

  /**
   * 滚动条服务,当滚动条到底部时,发送一个消息
   * jQueryLiteElement: 滚动的jquery元素
   * identification: 向上传播的事件名
   * transObj: 传给事件的对象
   */
  commonModule.factory('ScrollService', ['$rootScope', function($scope){
    return {
      init: function(jQueryLiteElement, identification, transObj){
        transObj = transObj || {};

        var elementDom  = jQueryLiteElement.get(0);
        var elementOffsetHeight, elementScrollTop, elementScrollHeight;

        elementDom.onmousewheel = function(){
          elementOffsetHeight = elementDom.offsetHeight;
          elementScrollHeight = elementDom.scrollHeight;
          elementScrollTop = elementDom.scrollTop;

          if(elementScrollTop + elementOffsetHeight === elementScrollHeight){
            $scope.$emit(identification,  transObj);
          }
        };

      }
    };

  }]);//End of --> ScrollService

  commonModule.factory('InitRightHeightService', ['$window', function($window){
    return {
      init: function(idName, heightPercent){
        heightPercent = heightPercent || 0.8;
        var screenHeight = $window.screen.height;

        //根据屏幕的高度(预设为80%),设置总任务内容的高度,
        var taskContent = $window.document.getElementById(idName);
        taskContent.style.minHeight = Math.floor((screenHeight * heightPercent)) + 'px';

      }
    };
  }]);//End of --> InitRightHeightService

  /*
    分页服务:用到C++/Java风格的继承机制
  */
  commonModule.factory('PaginationService', ['ClassHelperService',function(ClassHelperService){


    /**
     * 分页父类
     * @param aCurrentPage  当前显示的页码
     * @param aItemsPerPage  每页显示的项目数
     * @param aTotalItems    总共的项目数
     * @constructor  初始化私有数据成员currentPage,itemsPerPage,totalItems
     */
    function Pagination(aCurrentPage, aItemsPerPage, aTotalItems){
      this.currentPage = aCurrentPage;
      this.itemsPerPage = aItemsPerPage;
      this.totalItems = aTotalItems;

    }

    /*
      分类父类的成员方法
    */

    //设置当前页面为页码为1
    Pagination.prototype.setFirstCurrentPage = function(){
      this.currentPage = 1;
    };
    //当前页面页码自增1
    Pagination.prototype.addCurrentPage = function(){
      this.currentPage++;
    };
    //判断总项目数是否小于每页项目数
    Pagination.prototype.isLessPerItem = function(){
      return this.totalItems < this.itemsPerPage;
    };

    /**
     * 分页类的子类：有数字按钮的分页显示
     */
    function NumberPagination(aCurrentPage, aItemsPerPage, aTotalItems){
      //调用父类的构造函数初始化
      Pagination.call(this, aCurrentPage, aItemsPerPage, aTotalItems);
    }
    //使用 ClassHelperService 服务的extend方法进行继承
    ClassHelperService.extend(NumberPagination, Pagination);

    /**
     * 分页类的子类：有加载“圆圈”的分页显示
     */
    function LoadingPagination(aCurrentPage, aItemsPerPage, aTotalItems){
      Pagination.call(this, aCurrentPage, aItemsPerPage, aTotalItems);
      this.showLoading = false;    //判断是否显示加载“圈圈”的标志，false表示不显示
      this.finishLoading = false;  //判断是否加载完数据标志，false表示没有加载完
    }
    //使用 ClassHelperService 服务的extend方法进行继承
    ClassHelperService.extend(LoadingPagination, Pagination);

    //LoadingPagination私有方法：判断是否加载完全部项目
    LoadingPagination.prototype._isFinishLoading = function(){
      return this.currentPage == Math.ceil(this.totalItems/this.itemsPerPage);
    };

    //LoadingPagination公有方法：判断是否可以加载项目
    LoadingPagination.prototype.isAbleLoading = function(){
      return !this.isLessPerItem() && !this._isFinishLoading() && !this.showLoading;
    };

    //LoadingPagination公有方法：判断是否加载完成（但可能还有项目没显示出来）
    LoadingPagination.prototype.isShowFinish = function(){
      return !this.isLessPerItem() && this._isFinishLoading();
    };

    //LoadingPagination公有方法：当滚动条到底，加载新项目
    LoadingPagination.prototype.loadDataWhenToBottom = function(reloadList, reloadParamsObj){
      if(this.isAbleLoading()){
        this.showLoading = true;
        this.currentPage++;
        reloadList(reloadParamsObj);
      }
    };

    //LoadingPagination公有方法：获得showLoading的值
    LoadingPagination.prototype.getShowLoading = function(){
      return this.showLoading;
    };

    //LoadingPagination公有方法：设置showLoading的值
    LoadingPagination.prototype.setShowLoading = function(setShow){
      this.showLoading = setShow;
    };

    //LoadingPagination公有方法：获得finishLoading的值
    LoadingPagination.prototype.getFinishLoading = function(){
      return this.finishLoading;
    };

    LoadingPagination.prototype.setFinishLoading = function(setFinish){
      this.finishLoading = setFinish;
    };

    LoadingPagination.prototype.showFinish = function(){
      if(this.isShowFinish()) {
        this.finishLoading = true;
      }
    };


    function createPagination(options){
      if(options.type === 'NumberPagination'){
        return new NumberPagination(options.currentPage, options.itemsPerPage, options.totalItems);
      }else if(options.type === 'LoadingPagination'){
        return new LoadingPagination(options.currentPage, options.itemsPerPage, options.totalItems);
      }
    }

    /**
     * 返回封装好的对象
     * NumberPagination：有数字按钮的分页显示子类的对象
     * LoadingPagination：有加载“圆圈”的分页显示子类的对象
     */
    return {
    createPagination: createPagination
    };
  }]);//End of --> PaginationService


});